ICTSC2018 本戦 問題解説: コンテナなのに環境を変えたら動かなくなった!?

問題文

「時代はコンテナでオンプレだ!」が口癖である高圧的な上司は、最近、グローバルアドレスの持つ自宅のゲートウェイルータからフォワーディングを行うことで自宅サーバを全世界に公開しているみたいです。
ある日、上司は鬼のような形相で、「レンタルサーバーで動いていたDockerコンテナを自宅サーバに移行したらhttpが通らなくなった!pingは通るのにだぞ!!お前Docker詳しいって言ってたよな、サーバにだけsshで入れるようにしてやるから原因を究明して直してくれよ!」とあなたに無茶振りをしてきました。重ねて、「Dockerはどの環境でも動くものだろ!もともと動いてたんだからイメージに修正は加えるな!」とのことです。
上司との会話より、レンタルサーバーから自宅サーバに移行したコンテナは http://hpn.finals.ictsc へのGETリクエストが失敗するとエラー出力がされるものであり、また hpn.finals.ictsc は上司の自宅のゲートウェイルータに振られるグローバルアドレスに解決されることがわかりました。

またこの問題を先輩に相談したところ、先輩はニヤニヤした顔で「これは俺も家でサーバやるときにハマった事あるなぁ・・大きく2通りやり方があるんだが今回はRouterにログインできないから片方しか試せないかな・・・そうだ!明日の15時までに2通りの解決法を思いついた上で問題を解いたら焼き肉に連れてってやるよ!」と言ってきました。

上司の命令、そして焼き肉のために問題を解決してください。

  • 基準点
    • connection-check コンテナが、 connection successd! を返すように修正し、その解決法を上司に報告する。
  • 満点
    • 問題の解決法その2 (Routerにログインできる場合の解法) を述べ、原理を先輩に報告する。

トラブルの概要

内部から、ルータの外部アドレスを宛先とした内部への通信が成功しない

解説

グローバルアドレスを持つルータ (以下 ルータ) の内部のプライベートネットワークに存在するサーバ (以下 内部サーバ) がある環境において、外部からのルータ宛のパケットの宛先IPアドレスを内部サーバのアドレスに変換する (DNAT) 設定を行うことで、内部サーバのサービスを外部に提供できます。
ここで、ルータ宛のパケットに対しDNATのみ設定する場合、ある内部PCからルータのグローバルアドレスを宛先とした内部サーバへの通信を行うことはできません。なぜなら、内部PCから見た行きパケットの宛先アドレスと戻りパケットの送信元アドレスが一致しないためであるからです。

以下のどちらかの設定をすることでこの問題は解決します。
1. ドメイン名に対しローカルアドレスが解決されるよう設定
2. ルータにヘアピンNAT (NATループバック) の設定

サーバのみにログインできる当問題では、解法1を行うことで基準点を与えました。
解法1の具体的な例として、コンテナ起動 ( docker run ) 時に --add-host オプションを付与し hpn.finals.ictsc が内部サーバのアドレスに解決されるようにするといった方法があります。
なお、当問題の趣旨は「他人の環境を直す」ことであるため、再起動したら問題が再度起きる解法である場合減点としました。
ルータに触れられる場合の解法として、ルータに対しSNATおよびDNAT (ヘアピンNAT) の設定が必要であることを述べたチームに追加点を与えました。

この問題は、作問者が実際に自宅サーバを運用している際に起こった問題を再現しました。
作問者の家のルータにはヘアピンNATの設定項目が無かったため、内部にDNSサーバを建てた上で、内部マシン群はすべて内部DNSサーバに問い合わせることで解決しました。これにより、コンテナを起動時のオプション ( --add-host ) が必要なくなるといった利点があります。
これから自宅サーバを始める方に対して、この問題を通して得た知識が役に立てば幸いです。

回答例

  • 解法1: コンテナを一度 docker rm -f connection-check で落とした後、docker run -itd --name connection-check --add-host hpn.finals.ictsc:192.168.22.130 local/connection-check でコンテナを起動する。
  • 解法2: ヘアピンNAT (NATループバック) の設定が必要であることを述べる。

採点基準

  • 計160点
    • 基準点: 上司から与えられた問題の解決 (110点)
    • 満点: 先輩から与えられた問題の解決 (50点)